// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: develop dispatcher
// feature: content filter topic
// feature: discovery support client and server
// feature: dds support get remote participant vbs version and rm some print
// ------------------------------------------------------------------

#ifndef EDDS_CORE_POLICY__PARAMETERSERIALIZER_HPP_
#define EDDS_CORE_POLICY__PARAMETERSERIALIZER_HPP_

#include <edds/rtps/common/CDRMessage_t.h>
#include <ertps/utils/fixed_size_string.hpp>
#include <xmlparser/attributes/XMLRTPSParticipantAllocationAttributes.hpp>

#include <edds/dds/core/policy/ParameterList.hpp>

namespace evbs {
namespace edds {
namespace dds {

template <typename Parameter>
class ParameterSerializer {
 public:
    static inline bool add_common_to_cdr_message(const Parameter& parameter,
                                                 ertps::rtps::CDRMessage_t* const cdr_message) {
        bool valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, static_cast<uint16_t>(parameter.Pid));
        valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, parameter.length) && valid;
        return valid;
    }

    static bool add_to_cdr_message(const Parameter& parameter, ertps::rtps::CDRMessage_t* const cdr_message) {
        bool valid = add_common_to_cdr_message(parameter, cdr_message);
        valid = add_content_to_cdr_message(parameter, cdr_message) && valid;
        return valid;
    }

    static bool read_from_cdr_message(Parameter& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
                                      const uint16_t parameter_length) {
        bool valid;
        valid = read_content_from_cdr_message(parameter, cdr_message, parameter_length);
        return valid;
    }

    static uint32_t cdr_serialized_size(const Parameter& parameter) { return 4U + parameter.length; }

 private:
    static bool add_content_to_cdr_message(const Parameter&, ertps::rtps::CDRMessage_t*) {
        static_assert(sizeof(Parameter) == 0, "Not implemented");
        return false;
    }

    static bool read_content_from_cdr_message(Parameter&, ertps::rtps::CDRMessage_t* const, const uint16_t) {
        static_assert(sizeof(Parameter) == 0, "Not implemented");
        return false;
    }
};

template <>
class ParameterSerializer<Parameter_t> {
 public:
    static constexpr uint32_t PARAMETER_STATUS_SIZE = 8u;
    static constexpr uint32_t PARAMETER_KEY_SIZE = 20u;
    static constexpr uint32_t PARAMETER_SENTINEL_SIZE = 4u;
    static constexpr uint32_t PARAMETER_SAMPLE_IDENTITY_SIZE = 28u;

    static bool add_parameter_status(ertps::rtps::CDRMessage_t* const cdr_message, const ertps::rtps::octet status) {
        if ((cdr_message->pos + 8U) >= cdr_message->max_size) {
            return false;
        }
        (void)ertps::rtps::CDRMessage::addUInt16(cdr_message, edds::dds::PID_STATUS_INFO);
        (void)ertps::rtps::CDRMessage::addUInt16(cdr_message, 4U);
        (void)ertps::rtps::CDRMessage::addOctet(cdr_message, 0U);
        (void)ertps::rtps::CDRMessage::addOctet(cdr_message, 0U);
        (void)ertps::rtps::CDRMessage::addOctet(cdr_message, 0U);
        (void)ertps::rtps::CDRMessage::addOctet(cdr_message, status);
        return true;
    }

    static bool add_parameter_key(ertps::rtps::CDRMessage_t* const cdr_message,
                                  const ertps::rtps::InstanceHandle_t& iHandle) {
        if ((cdr_message->pos + 20U) >= cdr_message->max_size) {
            return false;
        }
        (void)ertps::rtps::CDRMessage::addUInt16(cdr_message, edds::dds::PID_KEY_HASH);
        (void)ertps::rtps::CDRMessage::addUInt16(cdr_message, 16U);
        (void)ertps::rtps::CDRMessage::addData(cdr_message, iHandle.value, 16U);
        return true;
    }

    static bool add_parameter_sentinel(ertps::rtps::CDRMessage_t* cdr_message) {
        if ((cdr_message->pos + 4U) > cdr_message->max_size) {
            std::cout << "add_parameter_sentinel fail." << std::endl;
            return false;
        }
        (void)ertps::rtps::CDRMessage::addUInt16(cdr_message, static_cast<uint16_t>(edds::dds::PID_SENTINEL));
        (void)ertps::rtps::CDRMessage::addUInt16(cdr_message, 0U);

        return true;
    }

    static bool add_parameter_sample_identity(ertps::rtps::CDRMessage_t* const cdr_message,
                                              const ertps::rtps::SampleIdentity& sample_id) {
        if ((cdr_message->pos + 28U) > cdr_message->max_size) {
            return false;
        }

        (void)ertps::rtps::CDRMessage::addUInt16(cdr_message, edds::dds::PID_RELATED_SAMPLE_IDENTITY);
        (void)ertps::rtps::CDRMessage::addUInt16(cdr_message, 24U);
        (void)ertps::rtps::CDRMessage::addData(cdr_message, sample_id.writer_guid().guidPrefix.value,
                                               ertps::rtps::GuidPrefix_t::size);
        (void)ertps::rtps::CDRMessage::addData(cdr_message, sample_id.writer_guid().entityId.value,
                                               ertps::rtps::EntityId_t::size);
        (void)ertps::rtps::CDRMessage::addInt32(cdr_message, sample_id.sequence_number().high);
        (void)ertps::rtps::CDRMessage::addUInt32(cdr_message, sample_id.sequence_number().low);
        return true;
    }

    static inline uint32_t cdr_serialized_size(const ertps::string_255& str) {
        // Size including NUL char at the end
        uint32_t str_siz = static_cast<uint32_t>(str.size()) + 1U;
        // Align to next 4 byte
        str_siz = (str_siz + 3U) & ~3U;
        // p_id + p_length + str_length + str_data
        return 2U + 2U + 4U + str_siz;
    }

    static inline uint32_t cdr_serialized_size(const ertps::rtps::Token& token) {
        // p_id + p_length
        uint32_t ret_val = 2U + 2U;

        // str_len + null_char + str_data
        ret_val += 4U + 1U + static_cast<uint32_t>(strlen(token.class_id().c_str()));
        // align
        ret_val = (ret_val + 3U) & (static_cast<uint32_t>(~3U));

        // properties
        ret_val += static_cast<uint32_t>(ertps::rtps::PropertyHelper::serialized_size(token.properties()));
        // align
        ret_val = (ret_val + 3U) & ~3U;

        // binary_properties
        ret_val += static_cast<uint32_t>(ertps::rtps::BinaryPropertyHelper::serialized_size(token.binary_properties()));
        // align
        ret_val = (ret_val + 3U) & ~3U;

        return ret_val;
    }
};

template <>
class ParameterSerializer<vbsutil::xmlparser::ContentFilterProperty> {
 public:
    static uint32_t cdr_serialized_size(const vbsutil::xmlparser::ContentFilterProperty& parameter) {
        uint32_t ret_val = 0;

        if (0 < parameter.filter_class_name.size() && 0 < parameter.content_filtered_topic_name.size() &&
            0 < parameter.related_topic_name.size() && 0 < parameter.filter_expression.size()) {
            // p_id + p_length
            ret_val = 2 + 2;
            // content_filtered_topic_name
            ret_val += cdr_serialized_size(parameter.content_filtered_topic_name);
            // related_topic_name
            ret_val += cdr_serialized_size(parameter.related_topic_name);
            // filter_class_name
            ret_val += cdr_serialized_size(parameter.filter_class_name);

            // filter_expression
            // str_len + null_char + str_data
            ret_val += 4 + 1 + static_cast<uint32_t>(parameter.filter_expression.size());
            // align
            ret_val = (ret_val + 3) & ~3;

            // expression_parameters
            // sequence length
            ret_val += 4;
            // Add all parameters
            for (const auto& param : parameter.expression_parameters) {
                ret_val += cdr_serialized_size(param);
            }
        }

        return ret_val;
    }

    static bool add_to_cdr_message(const vbsutil::xmlparser::ContentFilterProperty& parameter,
                                   ertps::rtps::CDRMessage_t* cdr_message) {
        bool valid = false;

        if (0 < parameter.filter_class_name.size() && 0 < parameter.content_filtered_topic_name.size() &&
            0 < parameter.related_topic_name.size() && 0 < parameter.filter_expression.size()) {
            // p_id + p_length
            uint32_t len = cdr_serialized_size(parameter);
            assert(4 < len && 0xFFFF >= (len - 4));
            valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, PID_CONTENT_FILTER_PROPERTY);
            valid &= ertps::rtps::CDRMessage::addUInt16(cdr_message, static_cast<uint16_t>(len - 4));
            // content_filtered_topic_name
            valid &= ertps::rtps::CDRMessage::add_string(cdr_message, parameter.content_filtered_topic_name);
            // related_topic_name
            valid &= ertps::rtps::CDRMessage::add_string(cdr_message, parameter.related_topic_name);
            // filter_class_name
            valid &= ertps::rtps::CDRMessage::add_string(cdr_message, parameter.filter_class_name);
            // filter_expression
            valid &= ertps::rtps::CDRMessage::add_string(cdr_message, parameter.filter_expression);

            // expression_parameters
            // sequence length
            uint32_t num_params = static_cast<uint32_t>(parameter.expression_parameters.size());
            valid &= ertps::rtps::CDRMessage::addUInt32(cdr_message, num_params);
            // Add all parameters
            for (const auto& param : parameter.expression_parameters) {
                valid &= ertps::rtps::CDRMessage::add_string(cdr_message, param);
            }
        }

        return valid;
    }

    static bool read_from_cdr_message(vbsutil::xmlparser::ContentFilterProperty& parameter,
                                      ertps::rtps::CDRMessage_t* cdr_message, const uint16_t parameter_length) {
        // Ensure incorrect length will result in parameter being cleared
        clear(parameter);

        // Validate minimum plength: 4 non-empty strings + number of expression parameters
        constexpr uint16_t min_plength = (4 * 8) + 4;
        if (parameter_length >= min_plength && (cdr_message->length - cdr_message->pos) > parameter_length) {
            bool valid = true;
            // Limit message length to parameter length, keeping old length to restore it later
            uint32_t old_msg_len = cdr_message->length;
            cdr_message->length = cdr_message->pos + parameter_length;

            // Read four strings
            valid = read_string(cdr_message, parameter.content_filtered_topic_name) &&
                    (0 < parameter.content_filtered_topic_name.size());
            if (valid) {
                valid =
                    read_string(cdr_message, parameter.related_topic_name) && (0 < parameter.related_topic_name.size());
            }
            if (valid) {
                valid =
                    read_string(cdr_message, parameter.filter_class_name) && (0 < parameter.filter_class_name.size());
            }
            if (valid) {
                valid = ertps::rtps::CDRMessage::readString(cdr_message, &parameter.filter_expression) &&
                        (0 < parameter.filter_expression.size());
            }

            // Read parameter sequence
            if (valid) {
                uint32_t num_parameters = 0;
                valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &num_parameters);
                if (valid) {
                    valid = (num_parameters <= 100) && (num_parameters <= parameter.expression_parameters.max_size());
                }
                if (valid) {
                    for (uint32_t i = 0; valid && i < num_parameters; ++i) {
                        auto p = parameter.expression_parameters.push_back({});
                        assert(nullptr != p);
                        valid = read_string(cdr_message, *p);
                    }
                }
            }

            cdr_message->length = old_msg_len;
            if (!valid) {
                clear(parameter);
            }

            return true;
        }

        return false;
    }

 private:
    static inline uint32_t cdr_serialized_size(const vbsutil::xmlparser::string_255& str) {
        // Size including NUL char at the end
        uint32_t str_siz = static_cast<uint32_t>(str.size()) + 1;
        // Align to next 4 byte
        str_siz = (str_siz + 3u) & ~3u;
        // str_length + str_data
        return 4u + str_siz;
    }

    static inline void clear(vbsutil::xmlparser::ContentFilterProperty& parameter) {
        parameter.filter_class_name = "";
        parameter.content_filtered_topic_name = "";
        parameter.related_topic_name = "";
        parameter.filter_expression = "";
        parameter.expression_parameters.clear();
    }

    static inline bool read_string(ertps::rtps::CDRMessage_t* cdr_message, vbsutil::xmlparser::string_255& str) {
        uint32_t str_size = 0;
        bool valid;
        valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &str_size);
        if (!valid || cdr_message->pos + str_size > cdr_message->length || str_size > str.max_size + 1) {
            return false;
        }

        str = "";
        // str_size == 1 would be for an empty string, as the NUL char is always serialized
        if (str_size > 1) {
            str = reinterpret_cast<const char*>(&(cdr_message->buffer[cdr_message->pos]));
        }
        cdr_message->pos += str_size;
        cdr_message->pos = (cdr_message->pos + 3u) & ~3u;

        return true;
    }
};

template <>
inline bool ParameterSerializer<ParameterLocator_t>::add_content_to_cdr_message(
    const ParameterLocator_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    return ertps::rtps::CDRMessage::addLocator(cdr_message, parameter.locator);
}

template <>
inline bool ParameterSerializer<ParameterLocator_t>::read_content_from_cdr_message(
    ParameterLocator_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_LOCATOR_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    return ertps::rtps::CDRMessage::readLocator(cdr_message, &parameter.locator);
}

template <>
inline bool ParameterSerializer<ParameterKey_t>::add_to_cdr_message(const ParameterKey_t& parameter,
                                                                    ertps::rtps::CDRMessage_t* const cdr_message) {
    return ParameterSerializer<Parameter_t>::add_parameter_key(cdr_message, parameter.key);
}

template <>
inline bool ParameterSerializer<ParameterKey_t>::read_content_from_cdr_message(
    ParameterKey_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_KEY_HASH_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    return ertps::rtps::CDRMessage::readData(cdr_message, parameter.key.value, PARAMETER_KEY_HASH_LENGTH);
}

template <>
inline uint32_t ParameterSerializer<ParameterString_t>::cdr_serialized_size(const ParameterString_t& parameter) {
    // Size including NUL char at the end
    uint32_t str_siz = static_cast<uint32_t>(parameter.size()) + 1U;
    // Align to next 4 byte
    str_siz = (str_siz + 3U) & ~3U;
    // p_id + p_length + str_length + str_data
    return 2U + 2U + 4U + str_siz;
}

template <>
inline bool ParameterSerializer<ParameterString_t>::add_to_cdr_message(const ParameterString_t& parameter,
                                                                       ertps::rtps::CDRMessage_t* const cdr_message) {
    if (parameter.size() == 0U) {
        return false;
    }
    bool valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, parameter.Pid);
    // Str size
    uint32_t str_siz = static_cast<uint32_t>(parameter.size() + 1U);
    uint16_t len = static_cast<uint16_t>(str_siz + 4U + 3U) & ~3U;
    valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, len) && valid;
    valid = ertps::rtps::CDRMessage::add_string(cdr_message, parameter.getName()) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterString_t>::read_content_from_cdr_message(
    ParameterString_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length > 256U) {
        return false;
    }

    parameter.length = parameter_length;
    ertps::string_255 aux;
    bool valid = ertps::rtps::CDRMessage::readString(cdr_message, &aux);
    parameter.setName(aux.c_str());
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterPort_t>::add_content_to_cdr_message(const ParameterPort_t& parameter,
                                                                             ertps::rtps::CDRMessage_t* cdr_message) {
    return ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.port);
}

template <>
inline bool ParameterSerializer<ParameterPort_t>::read_content_from_cdr_message(
    ParameterPort_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_PORT_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    return ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.port);
}

template <>
inline bool ParameterSerializer<ParameterGuid_t>::add_content_to_cdr_message(const ParameterGuid_t& parameter,
                                                                             ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addData(cdr_message, parameter.guid.guidPrefix.value, 12U);
    valid = ertps::rtps::CDRMessage::addData(cdr_message, parameter.guid.entityId.value, 4U) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterGuid_t>::read_content_from_cdr_message(
    ParameterGuid_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_GUID_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readData(cdr_message, parameter.guid.guidPrefix.value, 12U);
    valid = ertps::rtps::CDRMessage::readData(cdr_message, parameter.guid.entityId.value, 4U) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterProtocolVersion_t>::add_content_to_cdr_message(
    const ParameterProtocolVersion_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.protocolVersion.m_major);
    valid = ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.protocolVersion.m_minor) && valid;
    valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, 0U) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterProtocolVersion_t>::read_content_from_cdr_message(
    ParameterProtocolVersion_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
    const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_PROTOCOL_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.protocolVersion.m_major);
    valid = ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.protocolVersion.m_minor) && valid;
    cdr_message->pos += 2U;  // padding
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterVendorId_t>::add_content_to_cdr_message(
    const ParameterVendorId_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.vendorId[0]);
    valid = ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.vendorId[1]) && valid;
    valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, 0U) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterVendorId_t>::read_content_from_cdr_message(
    ParameterVendorId_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_VENDOR_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.vendorId[0]);
    valid = ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.vendorId[1]) && valid;
    cdr_message->pos += 2U;  // padding
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterDomainId_t>::add_content_to_cdr_message(
    const ParameterDomainId_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    return ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.domain_id);
}

template <>
inline bool ParameterSerializer<ParameterDomainId_t>::read_content_from_cdr_message(
    ParameterDomainId_t& parameter, ertps::rtps::CDRMessage_t* cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_DOMAINID_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    return ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.domain_id);
}

template <>
inline bool ParameterSerializer<ParameterVersion_t>::add_content_to_cdr_message(
    const ParameterVersion_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.evbsVersion[0]);
    valid &= ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.evbsVersion[1]);
    valid &= ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.evbsVersion[2]);
    valid &= ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.evbsVersion[3]);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterVersion_t>::read_content_from_cdr_message(
    ParameterVersion_t& parameter, ertps::rtps::CDRMessage_t* cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_EVBS_VERSION_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.evbsVersion[0]);
    valid &= ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.evbsVersion[1]);
    valid &= ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.evbsVersion[2]);
    valid &= ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.evbsVersion[3]);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterIP4Address_t>::add_content_to_cdr_message(
    const ParameterIP4Address_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    return ertps::rtps::CDRMessage::addData(cdr_message, parameter.address, 4U);
}

template <>
inline bool ParameterSerializer<ParameterIP4Address_t>::read_content_from_cdr_message(
    ParameterIP4Address_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_IP4_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    return ertps::rtps::CDRMessage::readData(cdr_message, parameter.address, 4U);
}

template <>
inline bool ParameterSerializer<ParameterBool_t>::add_content_to_cdr_message(const ParameterBool_t& parameter,
                                                                             ertps::rtps::CDRMessage_t* cdr_message) {
    ertps::rtps::octet val = parameter.value ? 1U : 0U;
    bool valid = ertps::rtps::CDRMessage::addOctet(cdr_message, val);
    valid = ertps::rtps::CDRMessage::addOctet(cdr_message, 0U) && valid;
    valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, 0U) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterBool_t>::read_content_from_cdr_message(
    ParameterBool_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_BOOL_LENGTH) {
        return false;
    }

    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readOctet(cdr_message, (ertps::rtps::octet*)&parameter.value);
    cdr_message->pos += 3U;  // padding
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterStatusInfo_t>::add_content_to_cdr_message(
    const ParameterStatusInfo_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, 0U);
    valid = ertps::rtps::CDRMessage::addOctet(cdr_message, 0U) && valid;
    valid = ertps::rtps::CDRMessage::addOctet(cdr_message, parameter.status) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterStatusInfo_t>::read_content_from_cdr_message(
    ParameterStatusInfo_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_STATUS_INFO_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    // octet status = msg.buffer[msg.pos + 3];
    ertps::rtps::octet tmp;
    // Remove the front three octets, take the fourth
    (void)ertps::rtps::CDRMessage::readOctet(cdr_message, &tmp);
    (void)ertps::rtps::CDRMessage::readOctet(cdr_message, &tmp);
    (void)ertps::rtps::CDRMessage::readOctet(cdr_message, &tmp);
    return ertps::rtps::CDRMessage::readOctet(cdr_message, &parameter.status);
}

template <>
inline bool ParameterSerializer<ParameterCount_t>::add_content_to_cdr_message(const ParameterCount_t& parameter,
                                                                              ertps::rtps::CDRMessage_t* cdr_message) {
    return ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.count);
}

template <>
inline bool ParameterSerializer<ParameterCount_t>::read_content_from_cdr_message(
    ParameterCount_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_COUNT_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    return ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.count);
}

template <>
inline bool ParameterSerializer<ParameterEntityId_t>::add_content_to_cdr_message(
    const ParameterEntityId_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    return ertps::rtps::CDRMessage::addEntityId(cdr_message, &parameter.entityId);
}

template <>
inline bool ParameterSerializer<ParameterEntityId_t>::read_content_from_cdr_message(
    ParameterEntityId_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_ENTITYID_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    return ertps::rtps::CDRMessage::readEntityId(cdr_message, &parameter.entityId);
}

template <>
inline bool ParameterSerializer<ParameterTime_t>::add_content_to_cdr_message(const ParameterTime_t& parameter,
                                                                             ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addInt32(cdr_message, parameter.time.seconds());
    valid = ertps::rtps::CDRMessage::addInt32(cdr_message, parameter.time.fraction()) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterTime_t>::read_content_from_cdr_message(
    ParameterTime_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_TIME_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    int32_t sec(0);
    bool valid = ertps::rtps::CDRMessage::readInt32(cdr_message, &sec);
    parameter.time.seconds(sec);
    uint32_t frac(0);
    valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &frac) && valid;
    parameter.time.fraction(frac);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterBuiltinEndpointSet_t>::add_content_to_cdr_message(
    const ParameterBuiltinEndpointSet_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    return ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.endpointSet);
}

template <>
inline bool ParameterSerializer<ParameterBuiltinEndpointSet_t>::read_content_from_cdr_message(
    ParameterBuiltinEndpointSet_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
    const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_BUILTINENDPOINTSET_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    return ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.endpointSet);
}

template <>
inline uint32_t ParameterSerializer<ParameterPropertyList_t>::cdr_serialized_size(
    const ParameterPropertyList_t& parameter) {
    // p_id + p_length + n_properties
    uint32_t ret_val = 2U + 2U + 4U;
    for (ParameterPropertyList_t::const_iterator it = parameter.begin(); it != parameter.end(); ++it) {
        // str_len + null_char + str_data
        ret_val += 4U + 1U + static_cast<uint32_t>(strlen(it->first().c_str()));
        // align
        ret_val = (ret_val + 3U) & ~3U;
        // str_len + null_char + str_data
        ret_val += 4U + 1U + static_cast<uint32_t>(strlen(it->second().c_str()));
        // align
        ret_val = (ret_val + 3U) & ~3U;
    }

    return ret_val;
}

template <>
inline bool ParameterSerializer<ParameterPropertyList_t>::add_to_cdr_message(
    const ParameterPropertyList_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, parameter.Pid);
    uint16_t pos_str = (uint16_t)cdr_message->pos;
    valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, parameter.length) && valid;
    valid = ertps::rtps::CDRMessage::addUInt32(cdr_message, (uint32_t)parameter.size()) && valid;
    for (ParameterPropertyList_t::const_iterator it = parameter.begin(); it != parameter.end(); ++it) {
        valid = ertps::rtps::CDRMessage::add_string(cdr_message, it->first()) && valid;
        valid = ertps::rtps::CDRMessage::add_string(cdr_message, it->second()) && valid;
    }
    uint16_t pos_param_end = (uint16_t)cdr_message->pos;
    uint16_t len = pos_param_end - pos_str - 2U;
    cdr_message->pos = pos_str;
    valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, len) && valid;
    cdr_message->pos = pos_param_end;
    cdr_message->length -= 2U;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterPropertyList_t>::read_content_from_cdr_message(
    ParameterPropertyList_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    if ((parameter.max_size() != 0U) && (parameter_length > parameter.max_size() + 4U)) {
        return false;
    }
    parameter.length = parameter_length;

    uint32_t pos_ref = cdr_message->pos;
    uint32_t max_pos = pos_ref + parameter_length;
    uint32_t remain = parameter_length;
    if ((max_pos > cdr_message->length) || (remain < sizeof(uint32_t))) {
        return false;
    }

    uint32_t num_properties = 0U;
    bool valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &num_properties);
    remain -= sizeof(uint32_t);
    if (!valid) {
        return false;
    }

    for (uint32_t i = 0U; i < num_properties; ++i) {
        uint32_t property1_size = 0U, alignment1 = 0U, property2_size = 0U, alignment2 = 0U, str1_pos = 0U,
                 str2_pos = 0U;

        // Read and validate size of property name
        remain = max_pos - cdr_message->pos;
        valid &= (remain >= sizeof(uint32_t)) && ertps::rtps::CDRMessage::readUInt32(cdr_message, &property1_size);
        remain -= sizeof(uint32_t);
        valid = valid && (remain >= property1_size);
        if (!valid) {
            return false;
        }

        str1_pos = cdr_message->pos;
        cdr_message->pos += property1_size;
        remain -= property1_size;
        alignment1 = ((property1_size + 3U) & ~3U) - property1_size;
        if (remain < alignment1) {
            return false;
        }
        cdr_message->pos += alignment1;
        remain -= alignment1;

        // Read and validate size of property value
        valid &= (remain >= sizeof(uint32_t)) && ertps::rtps::CDRMessage::readUInt32(cdr_message, &property2_size);
        remain -= sizeof(uint32_t);
        valid = valid && (remain >= property2_size);
        if (!valid) {
            return false;
        }

        str2_pos = cdr_message->pos;
        cdr_message->pos += property2_size;
        remain -= property2_size;
        alignment2 = ((property2_size + 3U) & ~3U) - property2_size;

        if (remain < alignment2) {
            return false;
        }
        cdr_message->pos += alignment2;
        remain -= alignment2;

        parameter.push_back(&cdr_message->buffer[str1_pos], property1_size, &cdr_message->buffer[str2_pos],
                            property2_size);
    }

    uint32_t length_diff = cdr_message->pos - pos_ref;
    valid = (parameter_length >= length_diff);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterSampleIdentity_t>::add_content_to_cdr_message(
    const ParameterSampleIdentity_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addData(cdr_message, parameter.sample_id.writer_guid().guidPrefix.value,
                                                  ertps::rtps::GuidPrefix_t::size);
    valid = ertps::rtps::CDRMessage::addData(cdr_message, parameter.sample_id.writer_guid().entityId.value,
                                             ertps::rtps::EntityId_t::size) &&
            valid;
    valid = ertps::rtps::CDRMessage::addInt32(cdr_message, parameter.sample_id.sequence_number().high) && valid;
    valid = ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.sample_id.sequence_number().low) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterSampleIdentity_t>::read_content_from_cdr_message(
    ParameterSampleIdentity_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
    const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_SAMPLEIDENTITY_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readData(cdr_message, parameter.sample_id.writer_guid().guidPrefix.value,
                                                   ertps::rtps::GuidPrefix_t::size);
    valid = ertps::rtps::CDRMessage::readData(cdr_message, parameter.sample_id.writer_guid().entityId.value,
                                              ertps::rtps::EntityId_t::size) &&
            valid;
    valid = ertps::rtps::CDRMessage::readInt32(cdr_message, &parameter.sample_id.sequence_number().high) && valid;
    valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.sample_id.sequence_number().low) && valid;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterE2eProtection_t>::add_content_to_cdr_message(
    const ParameterE2eProtection_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.e2eEnable);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterE2eProtection_t>::read_content_from_cdr_message(
    ParameterE2eProtection_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
    const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_E2E_ENABLE_LENGTH) {
        return false;
    }
    bool valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.e2eEnable);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterTransportType_t>::add_content_to_cdr_message(
    const ParameterTransportType_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.type);
    return valid;
}
template <>
inline bool ParameterSerializer<ParameterTransportType_t>::read_content_from_cdr_message(
    ParameterTransportType_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
    const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_TRANSPORTTYPE_LENGTH) {
        return false;
    }
    bool valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.type);
    return valid;
}
#if HAVE_SECURITY

template <>
inline uint32_t ParameterSerializer<ParameterToken_t>::cdr_serialized_size(const ParameterToken_t& parameter) {
    return ParameterSerializer<Parameter_t>::cdr_serialized_size(parameter.token);
}

template <>
inline bool ParameterSerializer<ParameterToken_t>::add_to_cdr_message(const ParameterToken_t& parameter,
                                                                      ertps::rtps::CDRMessage_t* const cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, parameter.Pid);
    uint16_t pos_str = (uint16_t)cdr_message->pos;
    valid &= ertps::rtps::CDRMessage::addUInt16(cdr_message, parameter.length);
    valid &= ertps::rtps::CDRMessage::addDataHolder(cdr_message, parameter.token);
    uint32_t align = (4U - cdr_message->pos % 4U) & 3U;  // align
    for (uint32_t count = 0U; count < align; ++count) {
        valid &= ertps::rtps::CDRMessage::addOctet(cdr_message, 0U);
    }
    uint16_t pos_param_end = (uint16_t)cdr_message->pos;
    uint16_t len = pos_param_end - pos_str - 2U;
    cdr_message->pos = pos_str;
    valid &= ertps::rtps::CDRMessage::addUInt16(cdr_message, len);
    cdr_message->pos = pos_param_end;
    cdr_message->length -= 2U;
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterToken_t>::read_content_from_cdr_message(
    ParameterToken_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message, const uint16_t parameter_length) {
    parameter.length = parameter_length;
    uint32_t pos_ref = cdr_message->pos;
    bool valid = ertps::rtps::CDRMessage::readDataHolder(cdr_message, parameter.token, parameter_length);
    uint32_t length_diff = cdr_message->pos - pos_ref;
    valid &= (parameter_length == length_diff);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterParticipantSecurityInfo_t>::add_content_to_cdr_message(
    const ParameterParticipantSecurityInfo_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.security_attributes);
    valid &= ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.plugin_security_attributes);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterParticipantSecurityInfo_t>::read_content_from_cdr_message(
    ParameterParticipantSecurityInfo_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
    const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_PARTICIPANT_SECURITY_INFO_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.security_attributes);
    valid &= ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.plugin_security_attributes);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterEndpointSecurityInfo_t>::add_content_to_cdr_message(
    const ParameterEndpointSecurityInfo_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.security_attributes);
    valid &= ertps::rtps::CDRMessage::addUInt32(cdr_message, parameter.plugin_security_attributes);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterEndpointSecurityInfo_t>::read_content_from_cdr_message(
    ParameterEndpointSecurityInfo_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
    const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_ENDPOINT_SECURITY_INFO_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.security_attributes);
    valid &= ertps::rtps::CDRMessage::readUInt32(cdr_message, &parameter.plugin_security_attributes);
    return valid;
}

#endif  // if HAVE_SECURITY

// ParameterHostIdPidInfo_t specialization
template <>
inline bool ParameterSerializer<ParameterHostIdPidInfo_t>::add_content_to_cdr_message(
    const ParameterHostIdPidInfo_t& parameter, ertps::rtps::CDRMessage_t* cdr_message) {
    bool valid = ertps::rtps::CDRMessage::addUInt16(cdr_message, parameter.hostId);
    valid &= ertps::rtps::CDRMessage::addUInt16(cdr_message, parameter.padding);
    valid &= ertps::rtps::CDRMessage::addInt32(cdr_message, parameter.pid);
    return valid;
}

template <>
inline bool ParameterSerializer<ParameterHostIdPidInfo_t>::read_content_from_cdr_message(
    ParameterHostIdPidInfo_t& parameter, ertps::rtps::CDRMessage_t* const cdr_message,
    const uint16_t parameter_length) {
    if (parameter_length != PARAMETER_HOSTID_PID_INFO_LENGTH) {
        return false;
    }
    parameter.length = parameter_length;
    bool valid = ertps::rtps::CDRMessage::readUInt16(cdr_message, &parameter.hostId);
    valid &= ertps::rtps::CDRMessage::readUInt16(cdr_message, &parameter.padding);
    valid &= ertps::rtps::CDRMessage::readInt32(cdr_message, &parameter.pid);
    return valid;
}

}  // namespace dds
}  // namespace edds
}  // namespace evbs

#endif  // EDDS_CORE_POLICY__PARAMETERSERIALIZER_HPP_
